package com.example.lsrsbackend.service.impl;

import com.example.lsrsbackend.exception.*;
import com.example.lsrsbackend.mapper.ReservationMapper;
import com.example.lsrsbackend.mapper.StudentMapper;
import com.example.lsrsbackend.mapper.SeatMapper;
import com.example.lsrsbackend.model.Reservation;
import com.example.lsrsbackend.model.Seat;
import com.example.lsrsbackend.model.DTO.ReservationRequest;
import com.example.lsrsbackend.model.Student;
import com.example.lsrsbackend.model.view.ReservationVO;
import com.example.lsrsbackend.service.ReservationService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.sql.Time;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;




@Slf4j
@Service
public class ReservationServiceImpl implements ReservationService {

    @Autowired
    private ReservationMapper reservationMapper;
    @Autowired
    private StudentMapper studentMapper;

    @Autowired
    private SeatMapper seatMapper;

    @Value("${library.seat.reservation.max-reservation-per-day:3}")
    private int maxReservationPerDay;

    @Value("${library.seat.reservation.checkin-timeout-minutes:15}")
    private int checkinTimeoutMinutes;

    @Override
    @Transactional
    public String reserveSeat(ReservationRequest request) {
        Long seatId = request.getSeatId();
        String username = request.getUsername();


        // 1. 检查预约限制
        if (!checkReservationLimit(String.valueOf(username), request.getDate())) {
            throw new ReservationException("今日预约次数已达上限");
        }

        // 2. 检查座位是否存在且可用
        Seat seat = seatMapper.selectById(seatId);
        if (seat == null) {
            throw new SeatException("座位不存在");
        }

        if (seat.getBaseStatus() != Seat.BaseStatus.AVAILABLE) {
            throw new SeatException("座位当前不可用");
        }

        // 3. 检查座位是否已被预约
        if (!isSeatAvailable(seatId, request.getDate())) {
            throw new SeatException("座位已被预约");
        }

        // 4. 检查时间冲突
        if (hasTimeConflict(seatId, request.getDate(), request.getStartTime(), request.getEndTime())) {
            throw new TimeConflictException("该时段已被预约");
        }

        // 5. 创建预约记录
        Reservation reservation = new Reservation();
        reservation.setSeatId(seatId);
        reservation.setUsername(username);
        reservation.setDate(request.getDate());
        reservation.setStartTime(request.getStartTime());
        reservation.setEndTime(request.getEndTime());
        reservation.setDurationHours(request.getDurationHours());
        reservation.setStatus(Reservation.Status.pending_fulfillment);
        reservation.setCreatedAt(new Date());
        reservation.setUpdatedAt(new Date());

        int result = reservationMapper.insert(reservation);
        if (result <= 0) {
            throw new ReservationException("预约失败，请重试");
        }


        //6 更新座位状态为已预约
        try {
            int updateResult = seatMapper.updateSeatStatus(seatId, Seat.BaseStatus.RESERVED);
            if (updateResult <= 0) {
                throw new ReservationException("更新座位状态失败");
            }
            log.info("座位状态更新成功: seatId={}, newStatus=RESERVED", seatId);
        } catch (Exception e) {
            log.error("更新座位状态失败: seatId={}", seatId, e);
            throw new ReservationException("更新座位状态失败");
        }

        log.info("座位预约成功: reservationId={}, seatId={}, username={}",
                reservation.getId(), seatId, username);


        return reservation.getId().toString();
    }




    @Override
    @Transactional
    public void cancelReservation(String reservationId, String username) throws JsonProcessingException {
        Long reservationIdLong = Long.parseLong(reservationId);


        Reservation reservation = reservationMapper.selectById(reservationIdLong);
        ObjectMapper mapper = new ObjectMapper();
        String jsonString = mapper.writeValueAsString(reservation);
        System.out.println(jsonString);
        if (reservation == null) {
            throw new ReservationException("预约记录不存在");
        }

        if (!reservation.getUsername().equals(username)) {
            throw new PermissionException("无权操作此预约");
        }



        // 更新预约状态为取消
        reservation.setStatus(Reservation.Status.cancelled);
        reservation.setUpdatedAt(new Date());

        int result = reservationMapper.update(reservation);
        if (result <= 0) {
            throw new ReservationException("取消预约失败");
        }

        // 恢复座位状态为可用
        try {
            int updateResult = seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.AVAILABLE);
            if (updateResult <= 0) {
                log.warn("恢复座位状态失败: seatId={}", reservation.getSeatId());
            } else {
                log.info("座位状态恢复为可用: seatId={}", reservation.getSeatId());
            }
        } catch (Exception e) {
            log.error("恢复座位状态异常: seatId={}", reservation.getSeatId(), e);
        }

        log.info("预约取消成功: reservationId={}, username={}", reservationId, username);
    }

    @Override
    @Transactional
    public void cancelReservationWithoutUserCheck(String reservationId) throws JsonProcessingException {
        Long reservationIdLong = Long.parseLong(reservationId);

        Reservation reservation = reservationMapper.selectById(reservationIdLong);
        if (reservation == null) {
            throw new ReservationException("预约记录不存在");
        }

        if (reservation.getStatus() != Reservation.Status.pending_fulfillment) {
            throw new ReservationException("只有待履约的预约才能取消");
        }

        // 更新预约状态为取消
        reservation.setStatus(Reservation.Status.cancelled);
        reservation.setUpdatedAt(new Date());

        int result = reservationMapper.update(reservation);
        if (result <= 0) {
            throw new ReservationException("取消预约失败");
        }

        // 恢复座位状态为可用
        try {
            int updateResult = seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.AVAILABLE);
            if (updateResult <= 0) {
                log.warn("恢复座位状态失败: seatId={}", reservation.getSeatId());
            } else {
                log.info("座位状态恢复为可用: seatId={}", reservation.getSeatId());
            }
        } catch (Exception e) {
            log.error("恢复座位状态异常: seatId={}", reservation.getSeatId(), e);
        }

        log.info("预约取消成功: reservationId={} (无需用户验证)", reservationId);
    }
    @Override
    @Transactional
    public void checkIn(String reservationId, String username) throws JsonProcessingException {
        Long reservationIdLong = Long.parseLong(reservationId);

        System.out.println(reservationIdLong);
        System.out.println(username);

        Date currentTime = new Date();

        Reservation reservation = reservationMapper.selectById(reservationIdLong);
        ObjectMapper mapper = new ObjectMapper();
        System.out.println(mapper.writeValueAsString(reservation));
        if (reservation == null) {
            throw new ReservationException("预约记录不存在");
        }

        if (!reservation.getUsername().equals(username)) {
            throw new PermissionException("无权操作此预约");
        }

        if (reservation.getStatus() != Reservation.Status.pending_fulfillment) {
            throw new ReservationException("只有待履约的预约才能签到");
        }

        // 检查是否超时（30分钟）
        long minutesLate = TimeUnit.MILLISECONDS.toMinutes(
                currentTime.getTime() - reservation.getStartTime().getTime() * 1000000);

        if (minutesLate > 30) {
            // 超时签到 -> 违约
            reservation.setStatus(Reservation.Status.breach);
            reservation.setUpdatedAt(new Date());
            reservationMapper.update(reservation);

            // 学生违约次数加一
            updateStudentBreachCount(username);
            throw new ReservationException("签到超时30分钟，预约已标记为违约");
        }

        // 正常签到 -> 已履约
        reservation.setStatus(Reservation.Status.fulfilled);
        reservation.setActualCheckinTime(new Date());
        reservation.setUpdatedAt(new Date());

        int result = reservationMapper.update(reservation);
        if (result <= 0) {
            throw new ReservationException("签到失败");
        }

        // 更新座位状态为使用中
        try {
            int updateResult = seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.OCCUPIED);
            if (updateResult <= 0) {
                log.warn("更新座位状态为使用中失败: seatId={}", reservation.getSeatId());
            } else {
                log.info("座位状态更新为使用中: seatId={}", reservation.getSeatId());
            }
        } catch (Exception e) {
            log.error("更新座位状态异常: seatId={}", reservation.getSeatId(), e);
        }

        log.info("签到成功: reservationId={}, username={}", reservationId, username);


    }

    @Override
    public boolean isUserFrozen(String username) {
        try {
            String status = studentMapper.selectStatusByUsername(username);
            // 假设 status 为 0 表示冻结状态
            return status != null && "frozen".equals(status);
        } catch (Exception e) {
            log.error("检查用户状态失败: username={}", username, e);
            return false; // 出错时默认允许预约
        }
    }

    @Override
    public List<ReservationVO> getUserReservations(String username, int days) {

        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_YEAR, -days);
        Date startDate = calendar.getTime();

        List<Reservation> reservations = reservationMapper.selectByUserAndDate(username, startDate);

        return reservations.stream().map(this::convertToVO).collect(Collectors.toList());
    }

    @Override
    @Transactional
    public void handleReservationTimeout(String reservationId) {
        Long reservationIdLong = Long.parseLong(reservationId);
        Reservation reservation = reservationMapper.selectById(reservationIdLong);

        if (reservation != null && reservation.getStatus() == Reservation.Status.breach) {
            int result = reservationMapper.updateToExpired(reservationIdLong);
            if (result > 0) {
                // 恢复座位状态为可用
                try {
                    seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.AVAILABLE);
                    log.info("超时预约座位状态恢复: reservationId={}, seatId={}", reservationId, reservation.getSeatId());
                } catch (Exception e) {
                    log.error("恢复超时预约座位状态失败: reservationId={}, seatId={}", reservationId, reservation.getSeatId(), e);
                }

                log.info("预约超时处理: reservationId={}", reservationId);
            }
        }
    }



    @Override
    @Transactional
    public void processTimeoutReservations() {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, -checkinTimeoutMinutes);
        Date timeoutTime = calendar.getTime();

        List<Reservation> timeoutReservations = reservationMapper.selectTimeoutReservations(timeoutTime);

        for (Reservation reservation : timeoutReservations) {
            reservationMapper.updateToExpired(reservation.getId());
            log.info("批量处理超时预约: reservationId={}", reservation.getId());
        }

        log.info("批量处理超时预约完成，共处理 {} 个预约", timeoutReservations.size());
    }

    @Override
    public boolean checkReservationLimit(String username, Date date) {
        //Long username = Long.parseLong(username);
        int count = reservationMapper.countByUserAndDate(username, date);
        return count < maxReservationPerDay;
    }
    @Override
    public List<ReservationVO> getAllReservations() {
        List<Reservation> reservations = reservationMapper.selectAll();
        return reservations.stream().map(this::convertToVO).collect(Collectors.toList());
    }

    private boolean isSeatAvailable(Long seatId, Date date) {
        List<Reservation> activeReservations = reservationMapper.selectActiveReservationsBySeatAndDate(seatId, date);
        return activeReservations.isEmpty();
    }

    private boolean hasTimeConflict(Long seatId, Date date, java.sql.Time startTime, java.sql.Time endTime) {
        return reservationMapper.countTimeConflict(seatId, date, startTime, endTime) > 0;
    }

    private boolean isCheckInTimeout(Reservation reservation) {
        long reservationTime = reservation.getCreatedAt().getTime();
        long currentTime = System.currentTimeMillis();
        long timeoutMillis = checkinTimeoutMinutes * 60 * 1000;

        return (currentTime - reservationTime) > timeoutMillis;
    }

    @Override
    @Transactional
    public void temporaryLeave(String reservationId, String username) {
        Long reservationIdLong = Long.parseLong(reservationId);


        Reservation reservation = reservationMapper.selectById(reservationIdLong);



        if (reservation == null) {
            throw new ReservationException("预约记录不存在");
        }

        if (!reservation.getUsername().equals(username)) {
            throw new PermissionException("无权操作此预约");
        }

        if (reservation.getStatus() != Reservation.Status.fulfilled) {
            throw new ReservationException("只有已履约的预约才能暂离");
        }

        // 更新座位状态为暂离
        try {
            int updateResult = seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.AWAY);
            if (updateResult <= 0) {
                throw new ReservationException("更新座位状态失败");
            }
            log.info("座位状态更新为暂离: seatId={}", reservation.getSeatId());
        } catch (Exception e) {
            log.error("更新座位状态失败: seatId={}", reservation.getSeatId(), e);
            throw new ReservationException("更新座位状态失败");
        }

        log.info("暂离成功: reservationId={}, username={}", reservationId, username);
    }

    @Override
    @Transactional
    public void returnFromLeave(String reservationId, String username) {
        Long reservationIdLong = Long.parseLong(reservationId);


        Reservation reservation = reservationMapper.selectById(reservationIdLong);
        if (reservation == null) {
            throw new ReservationException("预约记录不存在");
        }

        if (!reservation.getUsername().equals(username)) {
            throw new PermissionException("无权操作此预约");
        }

        if (reservation.getStatus() != Reservation.Status.fulfilled) {
            throw new ReservationException("只有已签到的预约才能返回");
        }

        // 检查座位当前状态是否为暂离
        Seat seat = seatMapper.selectById(reservation.getSeatId());
        if (seat == null || seat.getBaseStatus() != Seat.BaseStatus.AWAY) {
            throw new SeatException("座位不在暂离状态");
        }

        // 更新座位状态为使用中
        try {
            int updateResult = seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.OCCUPIED);
            if (updateResult <= 0) {
                throw new ReservationException("更新座位状态失败");
            }
            log.info("座位状态更新为使用中: seatId={}", reservation.getSeatId());
        } catch (Exception e) {
            log.error("更新座位状态失败: seatId={}", reservation.getSeatId(), e);
            throw new ReservationException("更新座位状态失败");
        }

        log.info("返回成功: reservationId={}, username={}", reservationId, username);
    }

    @Override
    @Transactional
    public void deleteReservation(String reservationId, String username) {
        Long reservationIdLong = Long.parseLong(reservationId);

        Reservation reservation = reservationMapper.selectById(reservationIdLong);
        if (reservation == null) {
            throw new ReservationException("预约记录不存在");
        }

        if (!reservation.getUsername().equals(username)) {
            throw new PermissionException("无权操作此预约");
        }

        // 只有特定状态的预约可以删除
        if (reservation.getStatus() != Reservation.Status.pending_fulfillment &&
                reservation.getStatus() != Reservation.Status.cancelled &&
                reservation.getStatus() != Reservation.Status.breach) {
            throw new ReservationException("当前状态的预约不能删除");
        }

        // 恢复座位状态为可用
        try {
            seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.AVAILABLE);
        } catch (Exception e) {
            log.warn("恢复座位状态失败: seatId={}", reservation.getSeatId(), e);
        }

        // 物理删除预约记录
        int result = reservationMapper.deleteById(reservationIdLong);
        if (result <= 0) {
            throw new ReservationException("删除预约失败");
        }

        log.info("预约删除成功: reservationId={}, username={}", reservationId, username);
    }

    private ReservationVO convertToVO(Reservation reservation) {
        ReservationVO vo = new ReservationVO();
        vo.setId(reservation.getId());
        vo.setSeatId(reservation.getSeatId());
        vo.setUsername(reservation.getUsername());
        vo.setDate(reservation.getDate());
        vo.setStartTime(reservation.getStartTime());
        vo.setEndTime(reservation.getEndTime());
        vo.setDurationHours(reservation.getDurationHours());
        vo.setStatus(reservation.getStatus());
        vo.setActualCheckinTime(reservation.getActualCheckinTime());
        vo.setActualCheckoutTime(reservation.getActualCheckoutTime());
        vo.setExtendCount(reservation.getExtendCount());
        vo.setCreatedAt(reservation.getCreatedAt());
        vo.setUpdatedAt(reservation.getUpdatedAt());

        // 设置前端展示字段
        vo.setStatusText(getStatusText(reservation.getStatus()));
        vo.setDateText(reservation.getDate().toString());
        vo.setTimeRange(reservation.getStartTime() + " - " + reservation.getEndTime());
        vo.setCanCancel(reservation.getStatus() == Reservation.Status.pending_fulfillment);
        vo.setCanCheckIn(reservation.getStatus() == Reservation.Status.pending_fulfillment);
        vo.setCanExtend(reservation.getStatus() == Reservation.Status.fulfilled);

        return vo;
    }


    /**
     * 根据时间规则更新单个预约状态
     */
    private void updateReservationStatusByTime(Reservation reservation) {
        Date currentTime = new Date();
        Date reservationStartTime = combineDateTime(reservation.getDate(), reservation.getStartTime());
        Date reservationEndTime = combineDateTime(reservation.getDate(), reservation.getEndTime());

        // 如果已经是取消或违约状态，不再更新
        if (reservation.getStatus() == Reservation.Status.cancelled ||
                reservation.getStatus() == Reservation.Status.breach) {
            return;
        }

        // 规则1：当前时间 < 预约开始时间 -> 待履约
        if (currentTime.before(reservationStartTime)) {
            if (reservation.getStatus() != Reservation.Status.pending_fulfillment) {
                reservation.setStatus(Reservation.Status.pending_fulfillment);
                reservation.setUpdatedAt(new Date());
                reservationMapper.update(reservation);
            }
            return;
        }

        // 规则2：预约开始时间 <= 当前时间 <= 预约结束时间
        if (!currentTime.after(reservationEndTime)) {
            // 已签到 -> 已履约
            if (reservation.getActualCheckinTime() != null) {
                if (reservation.getStatus() != Reservation.Status.fulfilled) {
                    reservation.setStatus(Reservation.Status.fulfilled);
                    reservation.setUpdatedAt(new Date());
                    reservationMapper.update(reservation);
                }
            }
            // 未签到，检查是否超时
            else {
                long minutesLate = TimeUnit.MILLISECONDS.toMinutes(
                        currentTime.getTime() - reservationStartTime.getTime());

                if (minutesLate > 30) {
                    // 超时30分钟 -> 违约
                    if (reservation.getStatus() != Reservation.Status.breach) {
                        reservation.setStatus(Reservation.Status.breach);
                        reservation.setUpdatedAt(new Date());
                        reservationMapper.update(reservation);

                        // 学生违约次数加一
                        updateStudentBreachCount(reservation.getUsername());

                        // 恢复座位状态为可用
                        seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.AVAILABLE);
                    }
                }
                // 如果未超时，保持待履约状态
            }
            return;
        }

        // 规则3：当前时间 > 预约结束时间
        if (currentTime.after(reservationEndTime)) {
            // 如果还是待履约状态且未签到，标记为违约
            if (reservation.getStatus() == Reservation.Status.pending_fulfillment &&
                    reservation.getActualCheckinTime() == null) {
                reservation.setStatus(Reservation.Status.breach);
                reservation.setUpdatedAt(new Date());
                reservationMapper.update(reservation);

                // 学生违约次数加一
                updateStudentBreachCount(reservation.getUsername());

                // 恢复座位状态为可用
                seatMapper.updateSeatStatus(reservation.getSeatId(), Seat.BaseStatus.AVAILABLE);
            }
            // 如果已签到，保持已履约状态
        }
    }

    /**
     * 合并日期和时间
     */
    private Date combineDateTime(Date date, Time time) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);

        Calendar timeCalendar = Calendar.getInstance();
        timeCalendar.setTime(time);

        calendar.set(Calendar.HOUR_OF_DAY, timeCalendar.get(Calendar.HOUR_OF_DAY));
        calendar.set(Calendar.MINUTE, timeCalendar.get(Calendar.MINUTE));
        calendar.set(Calendar.SECOND, timeCalendar.get(Calendar.SECOND));
        calendar.set(Calendar.MILLISECOND, 0);

        return calendar.getTime();

    }

    /**
     * 更新学生违约次数（需要学生表相关Mapper）
     */
    private void updateStudentBreachCount(String username) {
        try {
            // 这里需要调用学生Mapper来更新违约次数
            // studentMapper.incrementBreachCount(username);
            log.info("学生 {} 违约次数+1", username);
        } catch (Exception e) {
            log.error("更新学生违约次数失败: username={}", username, e);
        }
    }


    private String getStatusText(Reservation.Status status) {
        switch (status) {
            case pending_fulfillment: return "待履约";
            case fulfilled: return "已履约";
            case cancelled: return "已取消";
            case breach: return "违约";
            default: return "未知";
        }
    }



}